﻿#region << 版 本 注 释 >>
/*----------------------------------------------------------------
 * 版权所有 (c) 2022 北京超维景生物科技有限公司 保留所有权利。
 * CLR版本：4.0.30319.42000
 * 文件名：Kernel
 * 
 * 创建者：huangyang
 * 电子邮箱：huangyang@tvscope.cn
 * 创建时间：2023/2/15 15:36:56
 * 版本：V1.0.0
 * 描述：
 *
 * ----------------------------------------------------------------
 * 修改人：
 * 时间：
 * 修改说明：
 *
 * 版本：V1.0.1
 *----------------------------------------------------------------*/
#endregion << 版 本 注 释 >>

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using ImageK.Measure;
using ImageK.Plugin.Frame;
using ImageK.Process;

namespace ImageK.Gui
{
    /// <summary>
    /// A rectangular region of interest and superclass for the other ROI classes.
    /// </summary>
    public class Line:Roi
    {
        public int x1, y1, x2, y2;  // the line end points as integer coordinates, for compatibility only
        public double x1d, y1d, x2d, y2d;   // the line using sub-pixel coordinates
        protected double x1R, y1R, x2R, y2R;  // the line, relative to base of subpixel bounding rect 'bounds'
        protected double startxd, startyd;
        static bool widthChanged;
        private bool dragged;
        private int mouseUpCount;

		/** Creates a new straight line selection using the specified
			starting and ending offscreen integer coordinates. */
		public Line(int ox1, int oy1, int ox2, int oy2): this((double)ox1, (double)oy1, (double)ox2, (double)oy2)
		{
			
		}

		/** Creates a new straight line selection using the specified
			starting and ending offscreen double coordinates. */
		public Line(double ox1, double oy1, double ox2, double oy2):base((int)(ox1 + 0.5), (int) (oy1 + 0.5), 0, 0)
		{
			
			type = LINE;
			updateCoordinates(ox1, oy1, ox2, oy2);
			if (!(this is Arrow) && lineWidth > 1)
			    updateWideLine(lineWidth);
			updateClipRect();
			oldX = x; oldY = y; oldWidth = width; oldHeight = height;
			state = NORMAL;
		}

		/** Creates a new straight line selection using the specified
			starting and ending offscreen coordinates. */
		public static Line create(double x1, double y1, double x2, double y2)
		{
			return new Line(x1, y1, x2, y2);
		}

		/** Starts the process of creating a new user-generated straight line
			selection. 'sx' and 'sy' are screen coordinates that specify
			the start of the line. The user will determine the end of the line
			interactively using rubber banding. */
		public Line(int sx, int sy, ImagePlus imp):base(sx, sy, imp)
		{
			
			type = LINE;
			startxd = offScreenXD(sx);
			startyd = offScreenYD(sy);
			if (!magnificationForSubPixel())
			{
				startxd = Math.Round(startxd);
				startyd = Math.Round(startyd);
			}
			updateCoordinates(startxd, startyd, startxd, startyd);
			if (!(this is Arrow) && lineWidth > 1)
			updateWideLine(lineWidth);
		}

		/**
		* @deprecated
		* replaced by Line(int, int, int, int)
		*/
		public Line(int ox1, int oy1, int ox2, int oy2, ImagePlus imp):this(ox1, oy1, ox2, oy2)
        {
			setImage(imp);
		}

		protected override void grow(int sx, int sy)
		{ //mouseDragged
			drawLine(sx, sy);
			dragged = true;
		}

        public override void mouseMoved(MouseEventArgs e)
        {
            drawLine(e.X, e.Y);
		}

		public override void handleMouseUp(int screenX, int screenY)
		{
			mouseUpCount++;
			if (Prefs.enhancedLineTool && mouseUpCount == 1 && !dragged)
				return;
			state = NORMAL;
			if (imp == null) return;
			imp.draw(clipX - 5, clipY - 5, clipWidth + 10, clipHeight + 10);
			if (Recorder.OnRecord)
			{
				// string method = (this is Arrow)?"makeArrow":"makeLine";
				// Recorder.record(method, x1, y1, x2, y2);
			}
			if (getLength() == 0.0)
				imp.deleteRoi();
		}

		protected void drawLine(int sx, int sy)
		{
			double xend = offScreenXD(sx);
			double yend = offScreenYD(sy);
			if (xend < 0.0) xend = 0.0; if (yend < 0.0) yend = 0.0;
			if (xend > xMax) xend = xMax; if (yend > yMax) yend = yMax;
			double xstart = getXBase() + x1R, ystart = getYBase() + y1R;
			if (constrain)
			{
				int i = 0;
				double dy = Math.Abs(yend - ystart);
				double dx = Math.Abs(xend - xstart);
				double comp = dy / dx;
				for (; i < PI_SEARCH.Length; i++)
				{
					if (comp < PI_SEARCH[i])
					{
						break;
					}
				}
				if (i < PI_SEARCH.Length)
				{
					if (yend > ystart)
					{
						yend = ystart + dx * PI_MULT[i];
					}
					else
					{
						yend = ystart - dx * PI_MULT[i];
					}
				}
				else
				{
					xend = xstart;
				}
			}
			if (!magnificationForSubPixel() || IJ.controlKeyDown())
			{  //during creation, CTRL enforces integer coordinates
				xstart = Math.Round(xstart); ystart = Math.Round(ystart);
				xend = Math.Round(xend); yend = Math.Round(yend);
			}
			updateCoordinates(xstart, ystart, xend, yend);
			updateClipRect();
			imp.draw(clipX, clipY, clipWidth, clipHeight);
			oldX = x; oldY = y;
			oldWidth = width; oldHeight = height;
		}

		/** Used for angle searches in line ROI creation: tan = y/x for angle limits 1/2*45 degrees, and 3/2*45 deg */
		private static double[] PI_SEARCH = { Math.Tan(Math.PI / 8), Math.Tan((3 * Math.PI) / 8) };
		private static double[] PI_MULT = { 0, 1 }; // y/x for horizontal (0 degrees) and 45 deg

        public override void move(int sx, int sy)
		{
			int xNew = offScreenX(sx);
			int yNew = offScreenY(sy);
			x += (int)(xNew - startxd);
			y += (int)(yNew - startyd);
			clipboard = null;
			startxd = xNew;
			startyd = yNew;
			updateClipRect();
			if (ignoreClipRect)
				imp.draw();
			else
				imp.draw(clipX, clipY, clipWidth, clipHeight);
			oldX = x;
			oldY = y;
			oldWidth = width;
			oldHeight = height;
		}

		protected override void moveHandle(int sx, int sy)
		{
			if (constrain && activeHandle == 2)
			{  // constrain translation in 90deg steps
				int dx = sx - previousSX;
				int dy = sy - previousSY;
				if (Math.Abs(dx) > Math.Abs(dy))
					dy = 0;
				else
					dx = 0;
				sx = previousSX + dx;
				sy = previousSY + dy;
			}
			double ox = offScreenXD(sx);
			double oy = offScreenYD(sy);
			double x1d = getXBase() + x1R, y1d = getYBase() + y1R;
			double x2d = getXBase() + x2R, y2d = getYBase() + y2R;
			double length = Math.Sqrt(Math.Pow(x2d - x1d, 2) + Math.Pow(y2d - y1d,2));
			switch (activeHandle)
			{
				case 0:
					double dx = ox - x1d;
					double dy = oy - y1d;
					x1d = ox;
					y1d = oy;
					if (center)
					{
						x2d -= dx;
						y2d -= dy;
					}
					if (aspect)
					{
						double ratio = length / (Math.Sqrt(Math.Pow(x2d - x1d,2) + Math.Pow(y2d - y1d,2)));
						double xcd = x1d + (x2d - x1d) / 2;
						double ycd = y1d + (y2d - y1d) / 2;

						if (center)
						{
							x1d = xcd - ratio * (xcd - x1d);
							x2d = xcd + ratio * (x2d - xcd);
							y1d = ycd - ratio * (ycd - y1d);
							y2d = ycd + ratio * (y2d - ycd);
						}
						else
						{
							x1d = x2d - ratio * (x2d - x1d);
							y1d = y2d - ratio * (y2d - y1d);
						}

					}
					break;
				case 1:
					dx = ox - x2d;
					dy = oy - y2d;
					x2d = ox;
					y2d = oy;
					if (center)
					{
						x1d -= dx;
						y1d -= dy;
					}
					if (aspect)
					{
						double ratio = length / (Math.Sqrt((x2d - x1d) * (x2d - x1d) + (y2d - y1d) * (y2d - y1d)));
						double xcd = x1d + (x2d - x1d) / 2;
						double ycd = y1d + (y2d - y1d) / 2;

						if (center)
						{
							x1d = xcd - ratio * (xcd - x1d);
							x2d = xcd + ratio * (x2d - xcd);
							y1d = ycd - ratio * (ycd - y1d);
							y2d = ycd + ratio * (y2d - ycd);
						}
						else
						{
							x2d = x1d + ratio * (x2d - x1d);
							y2d = y1d + ratio * (y2d - y1d);
						}

					}
					break;
				case 2:
					dx = ox - (x1d + (x2d - x1d) / 2);
					dy = oy - (y1d + (y2d - y1d) / 2);
					x1d += dx; y1d += dy; x2d += dx; y2d += dy;
					break;
			}
			if (constrain)
			{
				double dx = Math.Abs(x1d - x2d);
				double dy = Math.Abs(y1d - y2d);
				double xcd = Math.Min(x1d, x2d) + dx / 2;
				double ycd = Math.Min(y1d, y2d) + dy / 2;

				//double ratio = length/(Math.sqrt((x2d-x1d)*(x2d-x1d) + (y2d-y1d)*(y2d-y1d)));
				if (activeHandle == 0)
				{
					if (dx >= dy)
					{
						if (aspect)
						{
							if (x2d > x1d) x1d = x2d - length;
							else x1d = x2d + length;
						}
						y1d = y2d;
						if (center)
						{
							y1d = y2d = ycd;
							if (aspect)
							{
								if (xcd > x1d)
								{
									x1d = xcd - length / 2;
									x2d = xcd + length / 2;
								}
								else
								{
									x1d = xcd + length / 2;
									x2d = xcd - length / 2;
								}
							}
						}
					}
					else
					{
						if (aspect)
						{
							if (y2d > y1d) y1d = y2d - length;
							else y1d = y2d + length;
						}
						x1d = x2d;
						if (center)
						{
							x1d = x2d = xcd;
							if (aspect)
							{
								if (ycd > y1d)
								{
									y1d = ycd - length / 2;
									y2d = ycd + length / 2;
								}
								else
								{
									y1d = ycd + length / 2;
									y2d = ycd - length / 2;
								}
							}
						}
					}
				}
				else if (activeHandle == 1)
				{
					if (dx >= dy)
					{
						if (aspect)
						{
							if (x1d > x2d) x2d = x1d - length;
							else x2d = x1d + length;
						}
						y2d = y1d;
						if (center)
						{
							y1d = y2d = ycd;
							if (aspect)
							{
								if (xcd > x1d)
								{
									x1d = xcd - length / 2;
									x2d = xcd + length / 2;
								}
								else
								{
									x1d = xcd + length / 2;
									x2d = xcd - length / 2;
								}
							}
						}
					}
					else
					{
						if (aspect)
						{
							if (y1d > y2d) y2d = y1d - length;
							else y2d = y1d + length;
						}
						x2d = x1d;
						if (center)
						{
							x1d = x2d = xcd;
							if (aspect)
							{
								if (ycd > y1d)
								{
									y1d = ycd - length / 2;
									y2d = ycd + length / 2;
								}
								else
								{
									y1d = ycd + length / 2;
									y2d = ycd - length / 2;
								}
							}
						}
					}
				}
			}
			//todo:
			// if (!magnificationForSubPixel())
			// {
			// 	x1d = Math.Round(x1d); y1d = Math.Round(y1d);
			// 	x2d = Math.Round(x2d); y2d = Math.Round(y2d);
			// }
			updateCoordinates(x1d, y1d, x2d, y2d);
			updateClipRect();
			imp.draw(clipX, clipY, clipWidth, clipHeight);
			oldX = x;
			oldY = y;
			oldWidth = width;
			oldHeight = height;
		}

		protected override void mouseDownInHandle(int handle, int sx, int sy)
		{
			base.mouseDownInHandle(handle, sx, sy); //sets state, activeHandle, previousSX&Y
			if (getStrokeWidth() <= 3)
				ic.Cursor = Cursors.Cross;
		}

		/** Sets the x1d, y1d, x2d, y2d line end points,
		 *  the (legacy) integer coordinates of the end points x1, y1, x2, y2
		 *  the 'bounds' subpixel rectangle of the Roi superclass (spanned by the end points),
		 *  the int x, y, width, height integer bounds of the superclass (these enclose
		 *  the 'draw' area for 1 pixel width), and
		 *  the coordinates x1R, y1R, x2R, y2R relative to the base x, y of the 'bounds' */
		void updateCoordinates(double x1d, double y1d, double x2d, double y2d)
		{
			this.x1d = x1d; this.y1d = y1d;
			this.x2d = x2d; this.y2d = y2d;
			RectangleF bounds = this.bounds;  //local variable (this.bounds may become null asynchronously upon nudge)
			if (bounds == null) bounds = new RectangleF();
			bounds.X = (float)Math.Min(x1d, x2d);
			bounds.Y = (float)Math.Min(y1d, y2d);
			bounds.Width = (float)Math.Abs(x2d - x1d);
			bounds.Height = (float)Math.Abs(y2d - y1d);
			setIntBounds(bounds); //sets x, y, width, height
			x1R = x1d - bounds.X; y1R = y1d - bounds.Y;
			x2R = x2d - bounds.X; y2R = y2d - bounds.Y;
			x1 = (int)x1d; y1 = (int)y1d; x2 = (int)x2d; y2 = (int)y2d;
			this.bounds = bounds;
		}

		/** Draws this line on the image. */
		public override void draw(Graphics g)
		{
			Color color = strokeColor != null ? strokeColor : ROIColor;
			bool isActiveOverlayRoiLocal = !overlay && isActiveOverlayRoi();
			mag = getMagnification();
			if (isActiveOverlayRoiLocal)
			{
				if (color == Color.Cyan)
					color = Color.Magenta;
				else
					color = Color.Cyan;
			}
			// g.setColor(color);
            var pen = new Pen(color);
			x1d = getXBase() + x1R; y1d = getYBase() + y1R; x2d = getXBase() + x2R; y2d = getYBase() + y2R;
			x1 = (int)x1d; y1 = (int)y1d; x2 = (int)x2d; y2 = (int)y2d;
			int sx1 = screenXD(x1d);
			int sy1 = screenYD(y1d);
			int sx2 = screenXD(x2d);
			int sy2 = screenYD(y2d);
			int sx3 = sx1 + (sx2 - sx1) / 2;
			int sy3 = sy1 + (sy2 - sy1) / 2;
			// Graphics2D g2d = (Graphics2D)g;
			// setRenderingHint(g2d);
			// bool cbar = overlay && mag < 1.0 && Math.Abs(getStrokeWidth() - CalibrationBar.STROKE_WIDTH) < 0.0001;
   //          if (stroke != null && !isActiveOverlayRoiLocal && !cbar)
   //          {
   //              g2d.setStroke(getScaledStroke());
			// }
			// else if (cbar)
   //          {
   //              g2d.setStroke(onePixelWide);
			// }
				
			// if (wideLine && !isActiveOverlayRoiLocal && !cbar)
			// {
			// 	double dx = sx2 - sx1;
			// 	double dy = sy2 - sy1;
			// 	double len = length(dx, dy);
			// 	dx *= 0.5 * mag / len;  //half-pixel extension, corresponding to getFloatPolygon or convertLineToArea
			// 	dy *= 0.5 * mag / len;
			// 	g2d.draw(new Line2D.Double(sx1 - dx, sy1 - dy, sx2 + dx, sy2 + dy));
			// }
			// else
				g.DrawLine(pen, sx1, sy1, sx2, sy2);
			if (wideLine && !overlay)
			{
				// g2d.setStroke(onePixelWide);
				// g.setColor(getColor());
				// g.drawLine(sx1, sy1, sx2, sy2);
			}
			if (!overlay)
			{
				handleColor = strokeColor != null ? strokeColor : ROIColor;
				drawHandle(g, sx1, sy1);
				handleColor = Color.White;
				drawHandle(g, sx2, sy2);
				drawHandle(g, sx3, sy3);
			}
			if (state != NORMAL)
				showStatus();
			if (updateFullWindow)
			{ updateFullWindow = false; imp.draw(); }
		}

		public override void showStatus()
		{
			// IJ.ShowStatus(imp.getLocationAsString((int)Math.round(x2d), (int)Math.round(y2d)) +
			// 		", angle=" + IJ.d2s(getAngle()) + ", length=" + IJ.d2s(getLength()));
		}

		public double getAngle()
        {
            return getFloatAngle(x1d, y1d, x2d, y2d);
        }

		/** Returns the length of this line. */
		public override double getLength()
		{
			if (imp == null || IJ.altKeyDown())
				return getRawLength();
			else
			{
				Calibration cal = imp.getCalibration();
				return Math.Sqrt(Math.Pow((x2d - x1d) * cal.pixelWidth, 2) + Math.Pow((y2d - y1d) * cal.pixelHeight,2));
			}
		}

		/** Returns the length of this line in pixels. */
		public double getRawLength()
		{
			return Math.Sqrt(Math.Pow(x2d - x1d,2) + Math.Pow(y2d - y1d,2));
		}

        /** Returns the pixel values along this line.
		 *  The line roi must have an associated ImagePlus */
        // public double[] getPixels()
        // {
        // 	double[] profile;
        // 	if (getStrokeWidth() <= 1)
        // 	{
        // 		ImageProcessor ip = imp.getProcessor();
        // 		profile = ip.getLine(x1d, y1d, x2d, y2d);
        // 	}
        // 	else
        // 	{
        // 		Straightener s = new Straightener();
        // 		ImageProcessor ip2 = s.straightenLine(imp, this, 0);
        // 		if (ip2 == null)
        // 			return new double[0];
        // 		int width = ip2.getWidth();
        // 		int height = ip2.getHeight();
        // 		if (ip2 is FloatProcessor)
        // 			return ProfilePlot.getColumnAverageProfile(new Rectangle(0, 0, width, height), ip2);
        // 		profile = new double[width];
        // 		double[] aLine;
        // 		ip2.setInterpolate(false);
        // 		for (int y = 0; y < height; y++)
        // 		{
        // 			aLine = ip2.getLine(0, y, width - 1, y);
        // 			for (int i = 0; i < width; i++)
        // 				profile[i] += aLine[i];
        // 		}
        // 		for (int i = 0; i < width; i++)
        // 			profile[i] /= height;
        // 	}
        // 	return profile;
        // }

        /** Returns, as a Polygon, the two points that define this line. */
        // public Polygon getPoints()
        // {
        // 	Polygon p = new Polygon();
        // 	p.addPoint((int)Math.round(x1d), (int)Math.round(y1d));
        // 	p.addPoint((int)Math.round(x2d), (int)Math.round(y2d));
        // 	return p;
        // }

        /** Returns, as a FloatPolygon, the two points that define this line. */
        public FloatPolygon getFloatPoints()
        {
            FloatPolygon p = new FloatPolygon();
            p.addPoint((float)x1d, (float)y1d);
            p.addPoint((float)x2d, (float)y2d);
            return p;
        }

        /** If the width of this line is less than or equal to one, returns the
		 * starting and ending coordinates as a 2-point Polygon, or, if
		 * the width is greater than one, returns an outline of the line as
		 * a 4-point Polygon.
		 * @see #getFloatPolygon
		 * @see #getPoints
		 */
        // public Polygon getPolygon()
        // {
        // 	FloatPolygon p = getFloatPolygon();
        // 	return new Polygon(toIntR(p.xpoints), toIntR(p.ypoints), p.npoints);
        // }

        /** If the width of this line is less than or equal to one, returns the
		 * starting and ending coordinates as a 2-point FloatPolygon, or, if
		 * the width is greater than one, returns an outline of the line as
		 * a 4-point FloatPolygon.
		 * @see #getFloatPoints
		 */
        public FloatPolygon getFloatPolygon()
		{
			return getFloatPolygon(getStrokeWidth());
		}

		/** Obsolete */
		public FloatPolygon getFloatPolygon(double strokeWidth)
		{
			FloatPolygon p = new FloatPolygon();
			// if (strokeWidth <= 1)
			// {
			// 	p.addPoint((float)x1d, (float)y1d);
			// 	p.addPoint((float)x2d, (float)y2d);
			// }
			// else
			// {
			// 	double dx = x2d - x1d;
			// 	double dy = y2d - y1d;
			// 	double len = length(dx, dy);
			// 	dx *= 0.5 / len;        // half unit vector in the direction of the line
			// 	dy *= 0.5 / len;        // when rotated 90 deg cw, this yields the vector (-dx, dy)
			// 	double p1x = x1d + 0.5 - dx + dy * strokeWidth;  //0.5 pxl shift: area vs. line coordinate convention
			// 	double p1y = y1d + 0.5 - dy - dx * strokeWidth;
			// 	double p2x = x1d + 0.5 - dx - dy * strokeWidth;
			// 	double p2y = y1d + 0.5 - dy + dx * strokeWidth;
			// 	double p3x = x2d + 0.5 + dx - dy * strokeWidth;
			// 	double p3y = y2d + 0.5 + dy + dx * strokeWidth;
			// 	double p4x = x2d + 0.5 + dx + dy * strokeWidth;
			// 	double p4y = y2d + 0.5 + dy - dx * strokeWidth;
			// 	p.addPoint((float)p1x, (float)p1y);
			// 	p.addPoint((float)p2x, (float)p2y);
			// 	p.addPoint((float)p3x, (float)p3y);
			// 	p.addPoint((float)p4x, (float)p4y);
			// }
			return p;
		}

		/** Returns the number of points in this selection; equivalent to getPolygon().npoints. */
		public int size()
		{
			return getStrokeWidth() <= 1 ? 2 : 4;
		}

		/** If the width of this line is less than or equal to one, draws the line.
		 *  Otherwise draws the outline of the area of this line */
		public void drawPixels(ImageProcessor ip)
		{
			// ip.setLineWidth(1);
			// double x = getXBase();
			// double y = getYBase();
			// x1d = getXBase() + x1R; y1d = getYBase() + y1R; x2d = getXBase() + x2R; y2d = getYBase() + y2R;
			// if (getStrokeWidth() <= 1)
			// {
			// 	ip.moveTo((int)Math.round(x1d), (int)Math.round(y1d));
			// 	ip.lineTo((int)Math.round(x2d), (int)Math.round(y2d));
			// }
			// else
			// {
			// 	Polygon p = getPolygon();
			// 	ip.drawPolygon(p);
			// 	updateFullWindow = true;
			// }
		}

		public override bool contains(int x, int y)
		{
			if (getStrokeWidth() > 1)
			{
				if ((x == x1 && y == y1) || (x == x2 && y == y2))
					return true;
                else
                {
                    throw new NotImplementedException();
                    // return getPolygon().contains(x, y);
                }
					
			}
			else
				return false;
		}

		public override void handleMouseDown(int sx, int sy)
		{
			base.handleMouseDown(sx, sy);
			startxd = ic.offScreenXD(sx);
			startyd = ic.offScreenYD(sy);
		}

		/** Returns a handle number if the specified screen coordinates are
			inside or near a handle, otherwise returns -1. */
		public int isHandle(int sx, int sy)
		{
			int size = HANDLE_SIZE + 5;
			if (getStrokeWidth() > 1) size += (int)Math.Log(getStrokeWidth());
			int halfSize = size / 2;
			int sx1 = screenXD(getXBase() + x1R) - halfSize;
			int sy1 = screenYD(getYBase() + y1R) - halfSize;
			int sx2 = screenXD(getXBase() + x2R) - halfSize;
			int sy2 = screenYD(getYBase() + y2R) - halfSize;
			int sx3 = sx1 + (sx2 - sx1) / 2 - 1;
			int sy3 = sy1 + (sy2 - sy1) / 2 - 1;
			if (sx >= sx1 && sx <= sx1 + size && sy >= sy1 && sy <= sy1 + size) return 0;
			if (sx >= sx2 && sx <= sx2 + size && sy >= sy2 && sy <= sy2 + size) return 1;
			if (sx >= sx3 && sx <= sx3 + size + 2 && sy >= sy3 && sy <= sy3 + size + 2) return 2;
			return -1;
		}

		/** Returns the default line width. Use getStrokeWidth()to
		 * get the width of a Line instance.
		 * @see #getStrokeWidth
		*/
		public static int getWidth()
		{
			return lineWidth;
		}

		/** Sets the default line width. Use setStrokeWidth()
		 * to set the width of a Line instance.
		 * @see #setStrokeWidth
		*/
		public static void setWidth(int w)
		{
			if (w < 1) w = 1;
			int max = 500;
			if (w > max)
			{
				ImagePlus imp2 = WindowManager.getCurrentImage();
				if (imp2 != null)
				{
					max = Math.Max(max, imp2.getWidth());
					max = Math.Max(max, imp2.getHeight());
				}
				if (w > max) w = max;
			}
			lineWidth = w;
			widthChanged = true;
		}

		/* Sets the width of this line. */
		public override void setStrokeWidth(float width)
		{
			base.setStrokeWidth(width);
			if (getStrokeColor() == Roi.getColor())
				wideLine = true;
		}

		protected int clipRectMargin()
		{
			return 4;
		}

		/** Nudge end point of line by one pixel. */
		public void nudgeCorner(int key)
		{
			// if (ic == null) return;
			// double inc = 1.0 / ic.getMagnification();
			// switch (key)
			// {
			// 	case KeyEvent.VK_UP: y2R -= inc; break;
			// 	case KeyEvent.VK_DOWN: y2R += inc; break;
			// 	case KeyEvent.VK_LEFT: x2R -= inc; break;
			// 	case KeyEvent.VK_RIGHT: x2R += inc; break;
			// }
			// grow(screenXD(x + x2R), screenYD(y + y2R));
			// notifyListeners(RoiListener.MOVED);
			// showStatus();
		}

		/** Always returns true. */
		public bool subPixelResolution()
		{
			return true;
		}

		public void setLocation(int x, int y)
		{
			setLocation((double)x, (double)y);
		}

		/** Sets the x coordinate of the leftmost and y coordinate of the topmost end point */
		public void setLocation(double x, double y)
		{
			updateCoordinates(x + x1R, y + y1R, x + x2R, y + y2R);
		}

		public FloatPolygon getRotationCenter()
		{
			double xcenter = x1d + (x2d - x1d) / 2.0;
			double ycenter = y1d + (y2d - y1d) / 2.0;
			FloatPolygon p = new FloatPolygon();
			//todo:
			// p.addPoint(xcenter, ycenter);
			return p;
		}

		/**
		 * Dedicated point iterator for thin lines.
		 * The iterator is based on (an improved version of) the algorithm used by
		 * the original method {@code ImageProcessor.getLine(double, double, double, double)}.
		 * Improvements are (a) that the endpoint is drawn too and (b) every line
		 * point is visited only once, duplicates are skipped.
		 *
		 * Author: Wilhelm Burger (04/2017)
		*/
		// public class PointIterator: IEnumerator<Point> 
  //       {
		//     private double x1, y1;
		//     private int n;
		//     private double xinc, yinc;
		//     private double x, y;
		//     private int u, v;
		//     private int u_prev, v_prev;
		//     private int i;
  //
		//     public PointIterator(Line line): this(line.x1d, line.y1d, line.x2d, line.y2d)
		// 	{
		// 	    ;
		//     }
  //
		//     public PointIterator(double x1, double y1, double x2, double y2)
		//     {
		// 	    this.x1 = x1;
		// 	    this.y1 = y1;
		// 	    double dx = x2 - x1;
		// 	    double dy = y2 - y1;
		// 	    this.n = (int)Math.Ceiling(Math.Sqrt(dx * dx + dy * dy));
		// 	    this.xinc = dx / n;
		// 	    this.yinc = dy / n;
		// 	    x = x1;
		// 	    y = y1;
		// 	    u = (int)Math.Round(x - 0.5);
		// 	    v = (int)Math.Round(y - 0.5);
		// 	    u_prev = int.MinValue;
		// 	    v_prev = int.MinValue;
		// 	    i = 0;
		//     }
  //
		//     public bool hasNext()
		//     {
		// 	    return i <= n;  // needs to be '<=' to include last segment (point)!
		//     }
  //
		//     public Point next()
		//     {
		// 	    if (i > n) throw new NoSuchElementException();
		// 	    Point p = new Point(u, v);  // the current (next) point
		// 	    moveToNext();
		// 	    return p;
		//     }
  //
		//     // move to next point by skipping duplicate points
		//     private void moveToNext()
		//     {
		// 	    do
		// 	    {
		// 		    i = i + 1;
		// 		    x = x1 + i * xinc;
		// 		    y = y1 + i * yinc;
		// 		    u_prev = u;
		// 		    v_prev = v;
		// 		    u = (int)Math.Round(x - 0.5);
		// 		    v = (int)Math.Round(y - 0.5);
		// 	    } while (i <= n && u == u_prev && v == v_prev);
		//     }
  //
		//     public void remove()
		//     {
		// 	    throw new UnsupportedOperationException();
		//     }
	 //    }
  //
	 //    public IEnumerator<Point> iterator()
	 //    {
		//     if (getStrokeWidth() <= 1.0)
		// 	    return new Line.PointIterator(this); // use the specific thin-line iterator
		//     else
		// 	    return base.iterator();    // fall back on Roi's iterator
	 //    }
    }
}
